###############################################################################
##########  PACKAGE:  api_utils.pm
##########  PURPOSE:  Provides access to miscellaneous utility functions
###############################################################################
package api_utils;

use strict;
use warnings;

use Data::Dumper qw(Dumper);
use Date::Parse;

use eve_const;

use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(
	write_cache_file
	read_cache_file
	cache_expired
	data_dump
	null2space
	str2isk
	padnum
	stripdec
	fail
	warn
	logger
	debug
	clear_cache
	clear_logs
	init_logs
	clear_dumps
	clear_outputs
	manage_api_profiles
	read_api_profiles
	write_api_profiles
	add_api_profile
	name2root
);


###############################################################################
##### WRITE XML DATA TO CACHE FILE
##### INPUTS:  filename, xmltext
##### OUTPUTS:  None
###############################################################################
sub write_cache_file {
	logger("--  --  --  BEGIN write_cache_file");
   my $filename = CACHE_DIR . $_[0] . ".cache";
	logger("--  --  --  --  FILENAME:  $filename");
   my $filedata = $_[1];
   open(OUTFILE, ">$filename") || fail("api_utils", "write_cache_file", "ERROR OPENING FILE $filename, $!");
   print OUTFILE $filedata;
   close(OUTFILE) || &warn("api_utils", "write_cache_file", "ERROR CLOSING FILE $filename, $!");
	logger("--  --  --  END write_cache_file");
}

###############################################################################
##### READ XML DATA FROM CACHE FILE
##### INPUTS:  filename
##### OUTPUTS:  filedata
###############################################################################
sub read_cache_file {
	logger("--  --  --  BEGIN read_cache_file");
   my $filename = CACHE_DIR . $_[0] . ".cache";
	logger("--  --  --  --  FILENAME:  $filename");
   my $filedata = '';
   unless (-s $filename) { return 0; }
   open(INFILE, $filename) || fail("api_utils", "read_cache_file", "ERROR OPENING FILE $filename, $!");
   while (<INFILE>) {
      chomp;
      $filedata .= $_."\n";
   }
   close(INFILE) || &warn("api_utils", "read_cache_file", "ERROR CLOSING FILE $filename, $!");
	logger("--  --  --  END read_cache_file");
   return $filedata;
}

###############################################################################
##### CHECK CACHE EXPIRATION
##### INPUTS:  date
##### OUTPUTS:  returns true if expired
###############################################################################
sub cache_expired {
	logger("--  --  --  BEGIN cache_expired");
	my $cachetime = str2time($_[0]);
	logger("--  --  --  --  CACHE:  $cachetime\tTIME:  " . time);
   if ( $cachetime > time ) {
		logger("--  --  --  END cache_expired -- return 1");
		return 1;
	} else {
		logger("--  --  --  END cache_expired -- return 0");
		return 0;
	}
}

###############################################################################
##### DUMP API DATA (formatted using Data::Dumper)
##### INPUTS:  filename, reference to data to dump
##### OUTPUTS:  None
###############################################################################
sub data_dump {
	logger("--  --  --  BEGIN data_dump");
	if (DEBUG > 1) {
		my $filename = DUMP_DIR . $_[0] . ".dump";
		logger("--  --  --  --  FILENAME:  $filename");
		open(DFILE, ">$filename") || fail("api_utils", "data_dump", "ERROR OPENING FILE $filename: $!");
		print DFILE Dumper($_[1]);
		close(DFILE) || &warn("api_utils", "data_dump", "ERROR CLOSING FILE $filename: $!");
	}
	logger("--  --  --  END data_dump");
}

###############################################################################
##### CONVERT NULL STRING TO A SINGLE ' '
##### INPUTS:  string
##### OUTPUTS:  string
###############################################################################
sub null2space {
	logger("--  --  --  SUB null2space");
	if ($_[0]) {
		return $_[0];
	} else {
		return ' ';
	}
}

###############################################################################
##### INSERT COMMAS INTO ISK AMOUNTS
##### INPUTS:  string
##### OUTPUTS:  string
###############################################################################
sub str2isk {
	logger("--  --  --  SUB str2isk");
	my $text = $_[0];
	$text = stripdec($text);
	$text = reverse $text;
	$text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
	return scalar reverse $text;
}

###############################################################################
##### ADD LEADING '0' TO NUMBERS BETWEEN 0-9
##### INPUTS:  string
##### OUTPUTS:  string
###############################################################################
sub padnum {
	logger("--  --  --  SUB padnum");
	my $tmp = $_[0];
	$tmp =~ s/^(\d)$/0$1/;
	return $tmp;
}

###############################################################################
##### STRIP DECIMAL POINT AND FRACTIONS FROM VALUES
##### INPUTS:  string
##### OUTPUTS:  string
###############################################################################
sub stripdec {
	logger("--  --  --  SUB stripdec");
	my $tmp = $_[0];
	$tmp =~ s/\.\d*$//;
	return $tmp;
}

###############################################################################
##### GENERATE DEBUG DATA AND TERMINATE PROGRAM ON ERROR
##### INPUTS:  modulename, procedurename, error message
##### OUTPUTS:  Terminates program
###############################################################################
sub fail {
	print "\n\n";
	print "****************************************\n";
	print "***** PROGRAM EXECUTION FAILURE ********\n";
	print "***** MODULE:     $_[0]\n";
	print "***** PROCEDURE:  $_[1]\n";
	print "***** MESSAGE:    $_[2]\n";
	print "****************************************\n";
	logger("\n\n");
	logger("****************************************");
	logger("***** PROGRAM EXECUTION FAILURE ********");
	logger("***** MODULE:     $_[0]");
	logger("***** PROCEDURE:  $_[1]");
	logger("***** MESSAGE:    $_[2]");
	logger("****************************************");
	debug(@_);
	die "$_[2]\n\n";
}

###############################################################################
##### GENERATE NON-FATAL ERROR MESSAGE
##### INPUTS:  modulename, procedurename, error message
##### OUTPUTS:  NONE
###############################################################################
sub warn {
	print "\n\n";
	print "***** WARNING\n";
	print "***** MODULE:     $_[0]\n";
	print "***** PROCEDURE:  $_[1]\n";
	print "***** MESSAGE:    $_[2]\n\n";
	logger("\n\n");
	logger("***** WARNING");
	logger("***** MODULE:     $_[0]");
	logger("***** PROCEDURE:  $_[1]");
	logger("***** MESSAGE:    $_[2]");
	debug(@_);
}

###############################################################################
##### APPEND MESSAGE TO LOGFILE
##### INPUTS:  string
##### OUTPUTS:  NONE
###############################################################################
sub logger {
	my $fname = LOG_DIR . LOG_FILE;
	open(LOG, ">>$fname") || fail("api_utils", "logger", "ERROR OPENING FILE $fname: $!");
	debug("LOGGED", "LOG", $_[0]);
	my $msg   = gmtime . "-- ".$_[0];
	print LOG "$msg\n";
	close(LOG) || &warn("api_utils", "logger", "ERROR CLOSING FILE $fname: $!");
}

###############################################################################
##### APPEND MESSAGE TO DEBUG FILE BASED ON DEBUG LEVEL
##### INPUTS:  modulename, procedurename, message, variables (optional)
##### OUTPUTS:  NONE
###############################################################################
sub debug {
	my $module = $_[0];
	my $proc   = $_[1];
	my $head   = gmtime . "-- $module\:\:$proc";
	my $msg    = gmtime . "-- ". $_[2];
	my @in     = @_;
	my $fname = LOG_DIR . DEBUG_FILE;
	open(BUG, ">>$fname") || fail("api_utils", "debug", "ERROR OPENING FILE $fname: $!");

	if (DEBUG == 0) {
		close(BUG);
		return 1;
	} elsif (DEBUG == 1) {
		print BUG "$head\n";
		print BUG "$msg\n";
	} elsif (DEBUG > 1) {
		print BUG "$head\n";
		print BUG "$msg\n";
		for (my $i=3; $i<=$#in; $i++) {
			print BUG Dumper($in[$i]);
		}
	}
	close(BUG) || &warn("api_utils", "debug", "ERROR CLOSING FILE $fname: $!");
}

###############################################################################
##### CLEAR THE CACHE DIRECTORY
##### INPUTS:  NONE
##### OUTPUTS:  NONE
###############################################################################
sub clear_cache {
	logger("--  --  --  SUB clear_cache");
	my $idir = CACHE_DIR;
	opendir(IDIR, "$idir") || fail("api_utils", "clear_cache", "ERROR OPENING DIR $idir: $!");
	while (my $tmp = readdir(IDIR)) {
		if ($tmp =~ /\.cache$/) {
			print "DELETED $tmp\n";
			logger("--  --  --  --  DELETED $tmp");
			unlink($idir.$tmp) || &warn("api_utils", "clear_cache", "ERROR DELETING FILE $tmp:  $!");
		}
	}
	closedir(IDIR) || &warn("api_utils", "clear_cache", "ERROR CLOSING DIR $idir: $!");
}

###############################################################################
##### CLEAR THE LOG DIRECTORY
##### INPUTS:  NONE
##### OUTPUTS:  NONE
###############################################################################
sub clear_logs {
	logger("--  --  --  SUB clear_logs");
	print "THIS ROUTINE IS NOT YET IMPLEMENTED\n";
}

###############################################################################
##### INITIALIZE LOG FILES
##### INPUTS:  NONE
##### OUTPUTS:  NONE
###############################################################################
sub init_logs {
	logger("--  --  --  SUB init_logs");
	my $lname = LOG_DIR . LOG_FILE;
	my $dname = LOG_DIR . DEBUG_FILE;
	unlink($lname) || &warn("api_utils", "init_logs", "ERROR DELETING FILE $lname: $!");
	unlink($dname) || &warn("api_utils", "init_logs", "ERROR DELETING FILE $dname: $!");
}

###############################################################################
##### CLEAR THE DUMPS DIRECTORY
##### INPUTS:  NONE
##### OUTPUTS:  NONE
###############################################################################
sub clear_dumps {
	logger("--  --  --  SUB clear_dumps");
	my $idir = DUMP_DIR;
	opendir(IDIR, "$idir") || fail("api_utils", "clear_dumps", "ERROR OPENING DIR $idir: $!");
	while (my $tmp = readdir(IDIR)) {
		if ($tmp =~ /\.dump$/) {
			print "DELETED $tmp\n";
			logger("--  --  --  --  DELETED $tmp");
			unlink($idir.$tmp) || &warn("api_utils", "clear_dumps", "ERROR DELETING FILE $tmp: $!");
		}
	}
	closedir(IDIR) || &warn("api_utils", "clear_dumps", "ERROR CLOSING DIR $idir: $!");
}

###############################################################################
##### CLEAR THE OUTPUT DIRECTORY
##### INPUTS:  NONE
##### OUTPUTS:  NONE
###############################################################################
sub clear_outputs {
	logger("--  --  --  SUB clear_outputs");
	my $idir = OUTPUT_DIR;
	opendir(IDIR, "$idir") || fail("api_utils", "clear_outputs", "ERROR OPENING DIR $idir: $!");
	while (my $tmp = readdir(IDIR)) {
		if ($tmp =~ /\.txt$/) {
			print "DELETED $tmp\n";
			logger("--  --  --  --  DELETED $tmp");
			unlink($idir.$tmp) || &warn("api_utils", "clear_dumps", "ERROR DELETING FILE $tmp: $!");
		}
	}
	closedir(IDIR) || &warn("api_utils", "clear_dumps", "ERROR CLOSING DIR $idir: $!");
}

###############################################################################
##### MANAGE API PROFILES
##### INPUTS:  NONE
##### OUTPUTS:  Profiles hash
###############################################################################
sub manage_api_profiles {
	logger("--  --  --  BEGIN manage_api_profiles");
	my $fname   = DATA_DIR . 'profiles.dat';
	my $profiles;
	my $done    = 0;
	my $input   = '';

	while (not $done) {
		print "PROFILE MANAGEMENT\n\n";
		if (-s $fname) {
			$profiles = read_api_profiles($fname);
			if (defined $profiles->{ACTIVE_PROFILE}) {
				print "ACTIVE PROFILE:  $profiles->{ACTIVE_PROFILE}\n";
			} else {
				print "ACTIVE PROFILE:\n";
			}
			print "\n";
			print "ENTER SELECTION:\n";
			my @pnames;
			my $i = 1;
			foreach my $name (sort(keys(%{$profiles}))) {
				if ($name ne 'ACTIVE_PROFILE') {
					print "\t$i  $name\t$profiles->{$name}{root}\n";
					$pnames[$i] = $name;
					$i++;
				}
			}
			print "\tA  Add new profile\n";
			print "\tD  Delete existing profile\n";
			print "\tX  Main menu\n";
			print "\n";
			print "ENTER SELECTION:  ";
			$input = <>;
			chomp $input;
			$input = ($input);
			if ($input =~ /^\d+$/) {
				$profiles->{ACTIVE_PROFILE} = $pnames[$input];
				write_api_profiles($fname, $profiles);
			} elsif ($input =~ /^a$/i) {
				my @j = &add_api_profile;
				$profiles->{$j[0]}{root} = $j[1];
				$profiles->{$j[0]}{id}   = $j[2];
				$profiles->{$j[0]}{key}  = $j[3];
				write_api_profiles($fname, $profiles);
			} elsif ($input =~ /^d$/i) {
				print "ENTER PROFILE NAME TO DELETE (CANNOT BE UNDONE):  ";
				$input = <>;
				chomp $input;
				if (exists $profiles->{$input}) {
					print "YOU HAVE CHOSEN TO DELETE PROFILE:  $input\n";
					print "ARE YOU SURE (y/n)?  ";
					my $tmp = <>;
					chomp $tmp;
					if ($tmp =~ /y/i) {
						delete $profiles->{$input};
						delete $profiles->{ACTIVE_PROFILE};
						write_api_profile($fname, $profiles);
					} else {
						print "NOT DELETED\n";
					}
				} else {
					print "INVALID INPUT:  $input NOT A VALID PROFILE NAME\n";
				}
			} elsif ($input =~ /^x$/i) {
				if (defined $profiles->{ACTIVE_PROFILE}) {
					write_api_profiles($fname, $profiles);
					data_dump("PROFILES", $profiles);
					logger("--  --  --  END manage_api_profiles");
					return $profiles;
				} else {
					print "YOU MUST SELECT A PROFILE TO USE FIRST\n";
				}
			} else {
				print "INVALID SELECTION:  $input\n";
			}
		} else {
			print "NO PROFILES FOUND. YOU MUST CREATE A PROFILE\n";
			my @j = &add_api_profile;
			$profiles->{$j[0]}{root}    = $j[1];
			$profiles->{$j[0]}{id}      = $j[2];
			$profiles->{$j[0]}{key}     = $j[3];
			$profiles->{ACTIVE_PROFILE} = $j[0];
			write_api_profiles($fname, $profiles);
		}
	}
}

###############################################################################
##### READ API PROFILES
##### INPUTS:  Filename
##### OUTPUTS:  Profiles hash
###############################################################################
sub read_api_profiles {
	my $fname = $_[0];
	my $profiles;

	logger("--  --  --  BEGIN read_api_profiles");
	open(IFILE, $fname) || fail("api_utils", "read_api_profiles", "ERROR OPENING FILE $fname:  $!");
	while (<IFILE>) {
		chomp;
		my @tmp = split(/::/, $_);
		if ($tmp[0] eq 'ACTIVE_PROFILE') {
			$profiles->{$tmp[0]} = $tmp[1];
		} else {
			$profiles->{$tmp[0]}{root} = $tmp[1];
			$profiles->{$tmp[0]}{id}   = $tmp[2];
			$profiles->{$tmp[0]}{key}  = $tmp[3];
		}
	}
	close(IFILE) || &warn("api_utils", "read_api_profiles", "ERROR CLOSING FILE $fname: $!");
	logger("--  --  --  END read_api_profiles");
	return $profiles;
}

###############################################################################
##### ADD API PROFILE
##### INPUTS:  NONE
##### OUTPUTS:  Profile name, root, id, key
###############################################################################
sub add_api_profile {
	my $profile = '';
	my $root    = '';
	my $id      = '';
	my $key     = '';
	my $done    = 0;

	logger("--  --  --  BEGIN add_api_profile");
	print "API KEYS ARE AVAILABLE AT http://www.eveonline.com/api/\n";
	print "MOST FUNCTIONS OF THIS UTILITY REQUIRE THE FULL API KEY\n";
	print "YOU WILL NEED YOUR USER ID (NOT LOGIN ID) AND API KEY\n";
	print "\n";
	while (not $done) {
		print "ENTER PROFILE NAME:  ";
		my $tmp = <>;
		chomp($tmp);
		if ($tmp =~ /^\w{2,}$/) {
			if ($tmp ne 'ACTIVE_PROFILE') {
				$profile = $tmp;
				$done = 1;
			} else {
				print "INVALID NAME -- CHOOSE ANOTHER:  $tmp\n";
			}
		} else {
			print "INVALID ENTRY -- MUST BE ALPHANUMERIC:  $tmp\n";
		}
	}
	$done = 0;
	while (not $done) {
		print "1  Tranquility (primary server)\n";
		print "2  Singularity (test server)\n";
		print "\n";
		print "CHOOSE SERVER:  ";
		my $tmp = <>;
		chomp($tmp);
		if ($tmp eq '1') {
			$root = 'TRANQUILITY';
			$done = 1;
		} elsif ($tmp eq '2') {
			$root = 'SINGULARITY';
			$done = 1;
		} else {
			print "INVALID SELECTION: $tmp\n";
		}
	}
	$done = 0;
	while (not $done) {
		print "ENTER USER ID:  ";
		my $tmp = <>;
		chomp($tmp);
		if ($tmp =~ /^\d{7}$/) {
			$id   = $tmp;
			$done = 1;
		} else {
			print "INVALID USER ID -- MUST BE 7 DIGITS LONG:  $tmp\n";
		}
	}
	$done = 0;
	while (not $done) {
		print "ENTER API KEY (FULL RECOMMENDED):  ";
		my $tmp = <>;
		chomp($tmp);
		if ($tmp =~ /^\w{64}$/) {
			$key  = $tmp;
			$done = 1;
		} else {
			print "INVALID KEY (MUST BE 64 CHARACTERS ALPHANUMERIC):  $tmp\n";
			print "TRY COPYING DIRECTLY FROM THE WEB SITE\n";
		}
	}
	logger("--  --  --  END add_api_profile");
	return $profile, $root, $id, $key;
}

###############################################################################
##### WRITE API PROFILES
##### INPUTS:  Filename, Profiles hash
##### OUTPUTS:  NONE
###############################################################################
sub write_api_profiles {
	my $fname    = $_[0];
	my $profiles = $_[1];

	logger("--  --  --  BEGIN write_api_profiles");
	data_dump("PROFILES", $profiles);
	open(OFILE, ">$fname") || fail("api_utils", "write_api_profiles", "ERROR WRITING FILE $fname:  $!");
	foreach my $name (sort(keys(%{$profiles}))) {
		if ($name ne 'ACTIVE_PROFILE') {
			my $txt = $name . '::' . $profiles->{$name}{root} . '::' . $profiles->{$name}{id} . '::' . $profiles->{$name}{key};
			print OFILE "$txt\n";
		} else {
			print OFILE "ACTIVE_PROFILE::$profiles->{$name}\n";
		}
	}
	close(OFILE) || &warn("api_utils", "write_api_profiles", "ERROR CLOSING FILE $fname: $!");
	logger("--  --  --  END write_api_profiles");
}

###############################################################################
##### CONVERT API SERVER NAME TO ADDRESS
##### INPUTS:  Server name
##### OUTPUTS:  Server address
###############################################################################
sub name2root {
	logger("--  --  --  SUB name2root");
	if ($_[0] =~ /tranquility/i) {
		return TRANQUILITY;
	} elsif ($_[0] =~ /singularity/i) {
		return SINGULARITY;
	} else {
		&warn("api_utils", "name2root", "INVALID SERVER NAME $_[0]");
		return 0;
	}
}

1;
